
/******************************************************************************
 * MODULE     : bridge_docrange.cpp
 * DESCRIPTION: Bridge between logical and physically typesetted document
 * COPYRIGHT  : (C) 1999  Joris van der Hoeven
 *******************************************************************************
 * This file contains a VERY HACKY way to accelerate the editing of
 * document nodes with many (> 32) children. It should be noticed that
 * the notification call-back methods do not behave as usual: the st tree
 * remains the st tree of the entire document tree and brs stands for
 * a reference to the corresponding bridges. The bridge_docrange structure
 * only behaves as usual for the my_exec_until, my_typeset_will_be_complete
 * and my_typeset methods.
 *******************************************************************************
 * This software falls under the GNU general public license version 3 or later.
 * It comes WITHOUT ANY WARRANTY WHATSOEVER. For details, see the file LICENSE
 * in the root directory or <http://www.gnu.org/licenses/gpl-3.0.html>.
 ******************************************************************************/

#include "bridge.hpp"
#include "tm_debug.hpp"

using namespace moebius;

#define ACC_THRESHOLD 32

/******************************************************************************
 * The docrange bridge structure
 ******************************************************************************/

class bridge_docrange_rep : public bridge_rep {
protected:
  array<bridge>  acc;
  array<bridge>& brs;
  array<int>     mid;
  int            begin, end;
  bool           divide;

public:
  bridge_docrange_rep (typesetter ttt, tree st, path ip, array<bridge>& brs,
                       int begin, int end, bool divide);
  void rebalance ();

  void notify_assign (path p, tree u);
  void notify_insert (path p, tree u);
  void notify_remove (path p, int nr);
  bool notify_macro (int type, string var, int l, path p, tree u);
  void notify_change ();

  void my_exec_until (path p);
  bool my_typeset_will_be_complete ();
  void my_typeset (int desired_status);
};

bridge
bridge_docrange (typesetter ttt, tree st, path ip, array<bridge>& brs,
                 int begin, int end, bool divide= false) {
  // cout << "Make range: " << begin << " -- " << end << "\n";
  return tm_new<bridge_docrange_rep> (ttt, st, ip, brs, begin, end, divide);
}

bridge_docrange_rep::bridge_docrange_rep (typesetter ttt, tree st, path ip,
                                          array<bridge>& brs2, int begin2,
                                          int end2, bool divide2)
    : bridge_rep (ttt, st, ip), brs (brs2), begin (begin2), end (end2),
      divide (divide2) {
  if (divide) {
    int i, n= ((end - begin - 1) / ACC_THRESHOLD) + 1;
    acc= array<bridge> (n);
    mid= array<int> (n + 1);
    for (i= 0; i < n; i++) {
      mid[i]    = begin + i * ACC_THRESHOLD;
      mid[i + 1]= min (mid[i] + ACC_THRESHOLD, end);
      acc[i]    = bridge_docrange (ttt, st, ip, brs, mid[i], mid[i + 1]);
    }
  }
}

void
bridge_docrange_rep::rebalance () {
  int           i, n= N (acc);
  array<bridge> acc2;
  array<int>    mid2;

  for (i= 0; i < n; i++) {
    // Compactify?
    if ((i < n - 1) && ((mid[i + 2] == mid[i + 1]) ||
                        (mid[i + 2] - mid[i] <= ACC_THRESHOLD))) {
      int start= i;
      while ((i < n - 1) && ((mid[i + 2] == mid[start + 1]) ||
                             (mid[i + 2] - mid[start] <= ACC_THRESHOLD)))
        i++;
      // cout << "  Compactify " << i-start << " at " << start << ", ";
      // if (mid[i+1] == mid[start+1]) cout << "suppress\n";
      // else cout << "compress\n";
      if (mid[i + 1] == mid[start + 1]) acc2 << acc[start];
      else acc2 << bridge_docrange (ttt, st, ip, brs, mid[start], mid[i + 1]);
      mid2 << mid[start];
    }

    // Expand?
    else if (mid[i + 1] - mid[i] > (7 * ACC_THRESHOLD / 4)) {
      int j, k= (mid[i + 1] - mid[i] - 1) / ACC_THRESHOLD + 1;
      // cout << "  Expand " << k << " at " << i << "\n";
      for (j= 0; j < k; j++) {
        int b= mid[i] + j * ACC_THRESHOLD;
        int e= min (b + ACC_THRESHOLD, mid[i + 1]);
        acc2 << bridge_docrange (ttt, st, ip, brs, b, e);
        mid2 << b;
      }
    }

    // OK
    else {
      acc2 << acc[i];
      mid2 << mid[i];
    }
  }

  mid2 << end;
  // if (mid2 != mid) cout << mid << " -> " << mid2 << "\n";
  acc= acc2;
  mid= mid2;
}

/******************************************************************************
 * Event notification
 ******************************************************************************/

void
bridge_docrange_rep::notify_assign (path p, tree u) {
  ASSERT (!is_nil (p), "erroneous nil path");
  if (divide) {
    int i, n= N (acc);
    for (i= 0; i < n; i++)
      if ((p->item >= mid[i]) && (p->item < mid[i + 1]))
        acc[i]->notify_assign (p, u);
  }
  status= CORRUPTED;
}

void
bridge_docrange_rep::notify_insert (path p, tree u) {
  // cout << "Notify insert " << p << ", " << N(u)
  //      << " [ " << begin << "--" << end << " ]\n";
  ASSERT (!is_nil (p), "erroneous nil path");
  if (p->item > end) {
    failed_error << "Notify insert " << u << " at " << p << "\n";
    TM_FAILED ("out of range");
  }
  if (p->item >= begin) status= CORRUPTED;
  else begin+= N (u);
  end+= N (u);

  if (divide) {
    int i, n= N (acc);
    for (i= 0; i < n; i++)
      if (p->item < mid[i + 1]) break;
    if (i == n) i--;
    for (; i < n; i++) {
      acc[i]->notify_insert (p, u);
      mid[i + 1]+= N (u);
    }
    // cout << "mid[ins,0]= " << mid << "\n";
    rebalance ();
    // cout << "mid[ins,1]= " << mid << "\n";
  }
}

void
bridge_docrange_rep::notify_remove (path p, int nr) {
  // cout << "Notify insert " << p << ", " << nr
  //      << " [ " << begin << "--" << end << " ]\n";
  ASSERT (!is_nil (p), "erroneous nil path");
  ASSERT (p->item < end, "out of range");
  if (p->item + nr > begin) {
    status= CORRUPTED;
    begin = min (begin, p->item);
    end   = max (end - nr, p->item);
  }
  else {
    begin-= nr;
    end-= nr;
  }

  if (divide) {
    int i, n= N (acc);
    for (i= 0; i < n; i++)
      if (p->item < mid[i + 1]) break;
    for (; i < n; i++) {
      acc[i]->notify_remove (p, nr);
      mid[i + 1]= max (mid[i + 1] - nr, p->item);
    }
    // cout << "mid[rem,0]= " << mid << "\n";
    rebalance ();
    // cout << "mid[rem,1]= " << mid << "\n";
  }
}

bool
bridge_docrange_rep::notify_macro (int type, string v, int l, path p, tree u) {
  (void) type;
  (void) v;
  (void) l;
  (void) p;
  (void) u;
  TM_FAILED ("method should never be called");
  return false;
}

void
bridge_docrange_rep::notify_change () {
  status= CORRUPTED;
  if (divide) {
    acc[0]->notify_change ();
    if (N (acc) > 1) acc[N (acc) - 1]->notify_change ();
  }
}

/******************************************************************************
 * Typesetting
 ******************************************************************************/

void
bridge_docrange_rep::my_exec_until (path p) {
  if (p->item < begin)
    ;
  else if ((p->item >= end) && ((status & VALID_MASK) == PROCESSED))
    env->patch_env (changes);
  else if (divide) {
    int i, n= N (acc);
    for (i= 0; i < n; i++)
      acc[i]->my_exec_until (p);
  }
  else {
    int i;
    for (i= begin; i < p->item; i++)
      brs[i]->exec_until (path (right_index (brs[i]->st)), true);
    if (p->item < end) brs[i]->exec_until (p->next);
  }
}

bool
bridge_docrange_rep::my_typeset_will_be_complete () {
  int i, n= N (acc);
  if (divide) {
    for (i= 0; i < n; i++)
      if (!acc[i]->my_typeset_will_be_complete ()) return false;
  }
  else {
    for (i= begin; i < end; i++)
      if (!brs[i]->my_typeset_will_be_complete ()) return false;
  }
  return true;
}

// static bool top_level= true;

void
bridge_docrange_rep::my_typeset (int desired_status) {
  int              i, n= N (acc);
  array<line_item> a= ttt->a;
  array<line_item> b= ttt->b;
  if (divide) {
    for (i= 0; i < n; i++) {
      int wanted=
          (i == n - 1 ? desired_status & WANTED_MASK : WANTED_PARAGRAPH);
      ttt->a= (i == 0 ? a : array<line_item> ());
      ttt->b= (i == n - 1 ? b : array<line_item> ());
      acc[i]->typeset (PROCESSED + wanted);
    }
  }
  else {
    // bool show= top_level;
    // bool old_top_level= top_level;
    // top_level= false;
    // if (show) cout << "Typeset range: " << begin << " -- " << end << "\n  ";
    for (i= begin; i < end; i++) {
      int wanted=
          (i == end - 1 ? desired_status & WANTED_MASK : WANTED_PARAGRAPH);
      ttt->a= (i == 0 ? a : array<line_item> ());
      ttt->b= (i == end - 1 ? b : array<line_item> ());
      // if (show) cout << (brs[i]->status == PROCESSED + wanted);
      // if (show) cout.flush ();
      brs[i]->typeset (PROCESSED + wanted);
    }
    // if (show) cout << "\n";
    // top_level= old_top_level;
  }
}
